/*
 *
 *  Created by John David Duncan on 6/29/09.
 *  Copyright 2009 Sun Microsystems. All rights reserved.
 *
 */

/*
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; version 2 of the License.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 */

#include "SQLnode.h"
#include "innodb.h"
#include <assert.h>

/* externs */
extern char * default_db;    // from main.cpp
extern "C" int describe_table(const char *);
extern bool innobase_is_started;
extern const char *constants[];


// SQLtable: CREATE TABLE and ALTER TABLE

void SQLtable::Dump(char **p, const char *s) {  
  QualifiedName qual(sch, obj, '.');
  report(p, s, "%s TABLE %s", constants[op], qual.name());
  
  if((op == op_create || op == op_alter)) { 
    report(p, s, op == op_create ? " (" : " ");
    
    for(unsigned int i = 0 ; i < list.size() ; i++) {
      if(i) report(p, s, ", ");
      list[i]->Dump(p, s);
    }
  }
  report(p, s, op == op_create ? ");" : ";");
}


int SQLtable::Run() {
  int err = DB_SUCCESS;
  ib_id_t table_id;

  if(!sch) sch = default_db;  

  QualifiedName qual(sch, obj, '/');

  if(! innobase_is_started) return e_START_THE_SERVER;
  switch(op) {
    case op_drop:
      // todo: handle case where there's an implied schema name
      err = ib_table_drop(qual.name());
      if(err == DB_SUCCESS) printf("TABLE %s DROPPED.\n", obj);
      break;
      
    case op_truncate:
      err = ib_table_truncate(obj, &table_id);
      if(err == DB_SUCCESS) printf("TABLE %s TRUNCATED.\n", obj);
      break;
      
    case op_describe:
      err = describe_table(qual.name());
      break;
      
    case op_create:
      err = create(qual.name());
      if(err == DB_SUCCESS) printf("Table created.\n");
     break;
      
    case op_alter:
    default:
      printf("Not implemented! %s %d \n", __FILE__,__LINE__);
  }
  return (int) err;
}


int SQLtable::create(const char *ibname) {
  int err = DB_SUCCESS;
  ib_id_t table_id = 0;
  ib_tbl_sch_t tschema = 0;
  ib_tbl_fmt_t tformat = IB_TBL_COMPACT;
     
  if(flags.redundant)  tformat = IB_TBL_REDUNDANT;
  if(flags.dynamic)    tformat = IB_TBL_DYNAMIC;
  if(flags.compressed) tformat = IB_TBL_COMPRESSED;
  
  if(size < 1024) size *= 1024;  // implied KB; see docs for KEY_BLOCK_SIZE
  
  err = ib_table_schema_create(ibname, &tschema, tformat, size);
  if(err != DB_SUCCESS) return err;
  
  /* Build the columns */
  for(unsigned int i = 0 ; i < list.size() ; i++) {
    SQLattr *atr = dynamic_cast<SQLattr *> (list[i]);
    if(atr->flags.is_column) err = atr->create_col(tschema);
    if(err != DB_SUCCESS) return err;
  }
      
  /* Build the indexes */
  for(unsigned int i = 0 ; i < list.size() ; i++) {
    SQLattr *atr = dynamic_cast<SQLattr *> (list[i]);
    if(atr->flags.is_index) err = atr->create_idx(tschema);
    if(err != DB_SUCCESS) return err;
  }
  
  /* Create the table */
  ib_trx_t trx = ib_trx_begin(IB_TRX_REPEATABLE_READ);
  err = ib_schema_lock_exclusive(trx);
  if(err != DB_SUCCESS) return err;
  
  err = ib_table_create(trx, tschema, &table_id);
  if(err != DB_SUCCESS) return err;
  
  err = ib_trx_commit(trx);
  ib_table_schema_delete(tschema);
  
  return err;  
}




// SQLattr: Columns and Indexes

void SQLattr::Dump(char **p, const char *s) {
  if(op) report(p, s, "%s ",constants[op]);
  if(flags.is_column) {
    report(p, s, "%s %s", atr, constants[type]);
    if(size) report(p, s, "(%d)", size);
    report(p, s, flags.nullable ? " " : " NOT NULL");
    if(flags.primary) report(p, s, " PRIMARY KEY");
  }
  else if(flags.is_index) {
    report(p, s, "%s ", constants[type]);
    if(atr) report(p, s, "%s", atr);
    report(p, s, "(");
    for(unsigned int i = 0 ; i < list.size() ; i++) {
      if(i) report(p, s, ",");
      SQLnode *part = list[i];
      report(p, s, "%s", part->atr);
      if(part->size) report(p, s, "(%d)", part->size);
    }
    report(p, s, ") ");
  }
  else assert("SQLattr is neither a column nor an index" == 0);
}


int SQLattr::Run() {
  // maybe it is impossible to get here? 
  printf("Not implemented! %s %d \n", __FILE__, __LINE__);
  return -1;
}


int SQLattr::create_col(ib_tbl_sch_t tschema) {
  ib_col_type_t col_type;
  unsigned int attributes = IB_COL_NONE;

  if(! flags.nullable) attributes |= IB_COL_NOT_NULL;
  if(flags.a_unsigned) attributes |= IB_COL_UNSIGNED ;

  switch(type) {
    case col_VARCHAR:
      col_type = IB_VARCHAR;
      break;      
    case col_CHAR:
      col_type = IB_CHAR;
      break;
    case col_BLOB:
      col_type = IB_BLOB;
      break;
    case col_INT:
      if(size == 0) size = 4;
      col_type = IB_INT;
      break;
    case col_FLOAT:
      col_type = IB_FLOAT;
      break;          
    case col_DOUBLE:
      col_type = IB_DOUBLE;
      break;
    default:
      assert("shouldnt be here" == 0);
      break;
  }

  int err = ib_table_schema_add_col(tschema, atr, col_type, 
                                    (ib_col_attr_t) attributes, 0, size);
  
  if(flags.primary) {  // one-column primary key
    ib_idx_sch_t pk;
    err = ib_table_schema_add_index(tschema, "PRIMARY", &pk);
    if(err != DB_SUCCESS) return err;
    err = ib_index_schema_add_col(pk, atr, 0);
    if(err != DB_SUCCESS) return err;
    err = ib_index_schema_set_clustered(pk);
  }
  return err;
}


int SQLattr::create_idx(ib_tbl_sch_t tschema) {
  int err;
  ib_idx_sch_t index;
  /* if the index has no explicit name, then it is named after its first col. */
  const char *name = atr ? atr : list[0]->atr; 
  err = ib_table_schema_add_index(tschema, name, &index);
  if(err != DB_SUCCESS) return err;
  
  if(flags.primary) {
    err = ib_index_schema_set_clustered(index);
    if(err != DB_SUCCESS) return err;
  }
  else if(type == idx_unique) {
    err = ib_index_schema_set_unique(index);
    if(err != DB_SUCCESS) return err;
  }


  /* Add the component fields to the index */
  for(unsigned int i = 0 ; i < list.size() ; i++) {
    SQLattr *col = dynamic_cast<SQLattr *> (list[i]);
    err = ib_index_schema_add_col(index, col->atr, col->size);
    if(err != DB_SUCCESS) return err;
  }
  
  return err;
}
